//
//  LogAs v 2.3.1 - logAs package
//  Russell Lowke, December 21st 2011
// 
//  Copyright (c) 2009-2011 Lowke Media
//  see http://www.lowkemedia.com for more information
//  see http://code.google.com/p/lowke/ for code repository
// 
//  Permission is hereby granted, free of charge, to any person obtaining a 
//  copy of this software and associated documentation files (the "Software"), 
//  to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//  and/or sell copies of the Software, and to permit persons to whom the 
//  Software is furnished to do so, subject to the following conditions:
// 
//  The above copyright notice and this permission notice shall be included in 
//  all copies or substantial portions of the Software.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER  
//  DEALINGS IN THE SOFTWARE. 
//
//

package com.lowke.logAs
{   
    import com.lowke.logAs.event.LogAsEvent;
    
    import flash.events.Event;
    import flash.events.EventDispatcher;
    
    // dispatched when a log occurs.
    [Event(name="logAsEvent", type="com.lowke.logAs.event.LogAsEvent")]
    
    /**
     *  LogAs package
     * 
     *  LogAs is useful in production environments where problems need to be logged
     *  and dealt with rather than causing a crash. In particular, LogAs allows 
     *  issues to be classed as either LOG_LEVEL_SEVERE, LOG_LEVEL_WARNING, LOG_LEVEL_INFO, or LOG_LEVEL_DEBUG. 
     * 
     * 
     *  Usage:
     * 
     *  LogAs can dispatch a classed log with an ID by using,
     * 
     *      LogAs.log(LogAs.LOG_LEVEL_SEVERE, "Your log message", 10000);
     * 
     *  Where the 1st parameter can be set to LogAs.LOG_LEVEL_SEVERE, LogAs.LOG_LEVEL_WARNING, 
     *  LogAs.LOG_LEVEL_INFO, LogAs.LOG_LEVEL_TRCAE or LogAs.LOG_LEVEL_DEBUG, and the 3rd parameter 
     *  allows for anoptional id value which defaults to 0.
     * 
     *  There are also shorthand static helper methods, LogAs.severe(), 
     *  LogAs.warning(), LogAs.info(), and LogAs.debug(), e.g.
     * 
     *      LogAs.severe("Your error message");
     *      LogAs.warning("Your warning message");
     *      LogAs.info("Your info message");
     *      LogAs.debug("Your debug message");
     *  
     * 
     *  You can set the logging level using LogAs.logLevel,
     *  
     *      LogAs.logLevel = LogAs.LOG_LEVEL_INFO;
     *  
     *  There are 5 logging levels, they are,
     * 
     *      LogAs.logLevel = LogAs.LOG_LEVEL_NONE               // don't log
     *      LogAs.logLevel = LogAs.LOG_LEVEL_SEVERE         // show only severe logs
     *      LogAs.logLevel = LogAs.LOG_LEVEL_WARNING            // show severe and warning logs
     *      LogAs.logLevel = LogAs.LOG_LEVEL_INFO               // show severe, warning and info logs
     *      LogAs.logLevel = LogAs.LOG_LEVEL_DEBUG          // show all logs
     * 
     *  By default the logLevel is set to LOG_LEVEL_INFO.
     * 
     *  LogAs will automatically trace() logs. You can prevent LogAs  
     *  from doing this by setting the traceLogs parameter to false,
     * 
     *      LogAs.traceLogs = false;
     * 
     * 
     *  You can also listen for logs using your own method,
     * 
     *      import com.lowke.logAs.events.LogAsEvent;
     * 
     *      LogAs.addEventListener(LogAsEvent.LOG_AS_EVENT, handleLogs);
     * 
     * 
     *  It's useful to use LogAs in conjuction with the Alert class.
     *  see the handleLogs() method in LogHandler.as for an example of this
     * 
     *  Typically error identifiers are comprised of a three digit prefix
     *  followed by a two digit number code.  e.g. "LDR02"
     * 
     * @author Russell Lowke
     * @langversion ActionScript 3.0
     * @playerversion Flash 10
     */
    
    public class LogAs extends EventDispatcher 
    {   
        // dispatched events
        public static const LOG_AS_EVENT:String = LogAsEvent.LOG_AS_EVENT;
        
        // log levels
        public static const LOG_LEVEL_NONE:uint     = 0;                        // don't log
        public static const LOG_LEVEL_SEVERE:uint   = LogAsEvent.SEVERE;        // show only severe logs
        public static const LOG_LEVEL_WARNING:uint  = LogAsEvent.WARNING;       // show severe and warning logs
        public static const LOG_LEVEL_INFO:uint     = LogAsEvent.INFO;          // show severe, warning and info logs
        public static const LOG_LEVEL_PRINT:uint    = LogAsEvent.PRINT;         // show severe, warning, info and print logs
        public static const LOG_LEVEL_DEBUG:uint    = LogAsEvent.DEBUG;         // show all logs        
        
        // static reference to LogAs singleton
        private static var _instance:LogAs;
        private static var _traceLogs:Boolean = true;                           // if true all logs are traced
        private static var _logLevel:uint;  
        
        private var _onceOnlyErrorsDisplayed:Object;                            // collection of once only errors that have been displayed so far
        
        public function LogAs() 
        {
            super();
            
            if (_instance != null) 
            {
                // throw error to prevent new instances of LogAs being created.
                throw new Error("LogAs is a singleton, it should never be created twice.\r" + 
                    "Use LogAs.instance to get a reference to LogAs.");
            }
            
            logLevel = LOG_LEVEL_PRINT;                 // default logging level is LOG_LEVEL_PRINT
            //  PRINT is a less spammy version of DEBUG for one-off testing
            
            _onceOnlyErrorsDisplayed = new Object();
        }
        
        
        //
        // Methods for triggering LogAs events
        //
        
        //
        // log() dispatches LogAsEvents, allowing an application
        // to deal with issues on its own terms, usually for logging purposes.
        public function log(logLevel:uint, 
                            message:String, 
                            id:String = null, 
                            showStackTrace:Boolean = false,
                            showOnlyOnce:Boolean = false):LogAsEvent 
        {
            
            var stackTrace:String;
            if (showStackTrace) 
            {
                try 
                {
                    // intentionally throw error so we can get a stack trace
                    throw new Error(LogAsEvent.toString(logLevel, message, id));
                } 
                catch (error:Error) 
                {
                    stackTrace = error.getStackTrace();
                }
            }
            
            var event:LogAsEvent = new LogAsEvent(LOG_AS_EVENT, message, logLevel, id, stackTrace);
            
            // some errors need be displayed only once, otherwise they get too spammy
            if (showOnlyOnce) 
            {
                if (_onceOnlyErrorsDisplayed[id]) 
                {
                    // this error has already been displayed
                    return event;
                } 
                else 
                {
                    // tag this error as displayed
                    _onceOnlyErrorsDisplayed[id] = true;
                }
            }
            
            if (logLevel <= _logLevel) 
            {
                if (_traceLogs) 
                {
                    trace(event);
                }
                
                // dispatch a LogAsEvent event for others to listen to
                dispatchEvent(event);
            }
            
            return event;
        }
        
        //
        // static helper methods
        
        // by default severe logs show the stack trace
        public static function severe(message:String, 
                                      id:String = null, 
                                      stackTrace:Boolean = true,
                                      showOnlyOnce:Boolean = false):LogAsEvent      
        {
            return instance.log(LOG_LEVEL_SEVERE, message, id, stackTrace, showOnlyOnce);
        }
        
        public static function warning(message:String, 
                                       id:String = null, 
                                       stackTrace:Boolean = false,
                                       showOnlyOnce:Boolean = false):LogAsEvent 
        {
            return instance.log(LOG_LEVEL_WARNING, message, id, stackTrace, showOnlyOnce);
        }
        
        public static function info(message:String, 
                                    id:String = null, 
                                    stackTrace:Boolean = false,
                                    showOnlyOnce:Boolean = false):LogAsEvent 
        {
            return instance.log(LOG_LEVEL_INFO, message, id, stackTrace, showOnlyOnce);
        }
        
        public static function print(message:String, 
                                     id:String = null, 
                                     stackTrace:Boolean = false,
                                     showOnlyOnce:Boolean = false):LogAsEvent 
        {
            return instance.log(LOG_LEVEL_PRINT, message, id, stackTrace, showOnlyOnce);
        }
        
        public static function debug(message:String, 
                                     id:String = null, 
                                     stackTrace:Boolean = false,
                                     showOnlyOnce:Boolean = false):LogAsEvent 
        {
            return instance.log(LOG_LEVEL_DEBUG, message, id, stackTrace, showOnlyOnce);
        }
        
        //
        // pass event listeners through to singleton
        public static function addWeakListener(type:String, 
                                               listener:Function, 
                                               useCapture:Boolean = false, 
                                               priority:int = 0, 
                                               useWeakReference:Boolean = true):void 
        {
            instance.addEventListener(type, listener, useCapture, priority, useWeakReference);
        }
        
        public static function addEventListener(type:String,
                                                listener:Function,
                                                useCapture:Boolean = false,
                                                priority:int = 0,
                                                useWeakReference:Boolean = false):void 
        {
            instance.addEventListener(type, listener, useCapture, priority, useWeakReference);
        }
        
        public static function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void 
        {
            instance.removeEventListener(type, listener, useCapture);
        }
        
        public static function dispatchEvent(event:Event):Boolean
        {
            return instance.dispatchEvent(event);
        }
        
        public static function hasEventListener(type:String):Boolean
        {
            return instance.hasEventListener(type);
        }
        
        public static function willTrigger(type:String):Boolean
        {
            return instance.willTrigger(type);
        }
        
        //
        // accessors and mutators
        //
        
        public function set traceLogs(value:Boolean):void 
        {
            if (value != _logLevel) 
            {
                if (value) 
                {
                    trace("LogAs traceLogs switched ON");
                } 
                else 
                {
                    trace("LogAs traceLogs switched OFF");
                }
                _traceLogs = value; 
            }
        }
        
        public function set logLevel(value:uint):void 
        { 
            if (value != _logLevel) 
            {
                switch(value) 
                {
                    case LOG_LEVEL_NONE:
                        trace("LogAs logging level set to LOG_LEVEL_NONE");
                        break;
                    case LOG_LEVEL_SEVERE:
                        trace("LogAs logging level set to LOG_LEVEL_SEVERE");
                        break;
                    case LOG_LEVEL_WARNING:
                        trace("LogAs logging level set to LOG_LEVEL_WARNING");
                        break;
                    case LOG_LEVEL_INFO:
                        trace("LogAs logging level set to LOG_LEVEL_INFO");
                        break;
                    case LOG_LEVEL_PRINT:
                        trace("LogAs logging level set to LOG_LEVEL_PRINT");
                        break;
                    case LOG_LEVEL_DEBUG:
                        trace("LogAs logging level set to LOG_LEVEL_DEBUG");
                        break;
                    default:
                        throw new Error("LogAs cannot change logging level to " + value);
                }
                _logLevel = value;
            }
        }
        
        public static function set traceLogs(value:Boolean):void    { instance.traceLogs = value; }
        public static function set logLevel(value:uint):void        { instance.logLevel = value; }
        
        public static function get traceLogs():Boolean              { return _traceLogs; }
        public static function get logLevel():uint                  { return _logLevel; }
        
        
        /** Gives a reference to the AssetLoader singleton. */
        public static function get instance():LogAs 
        {
            if (! _instance) 
            {
                _instance = new LogAs();
            }
            return _instance;
        }
    }
}